#include <sys/epoll.h>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <string>
#include <functional>
#include <unordered_map>
#include <memory>
#include "Socket.hpp"
#include "Epoller.hpp"
#include "SetNoWaitOrDie.hpp"

class Connection;

using func_t = std::function<void(std::shared_ptr<Connection>)>;

class Connection
{
public:
    Connection(int fd, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb, \
            std::string& ip, uint16_t port)
        :_ip(ip), _port(port), _fd(fd),
        _event(event),
        _recv_cb(recv_cb),
        _send_cb(send_cb),
        _except_cb(except_cb)
    { }

    int Fd()
    {
        return _fd;
    }

    std::string& InBuff()
    {
        return _inbuff;
    }

    std::string& OutBuff()
    {
        return _outbuff;
    }

    ~Connection() { }

private:
    std::string _ip;
    uint16_t _port;

private:
    int _fd;
    std::string _inbuff;
    std::string _outbuff;

public:
    uint32_t _event;
    func_t _recv_cb;
    func_t _send_cb;
    func_t _except_cb;
};

class EpollServer
{
    static const int n = 64;

public:
    EpollServer(int port, func_t upperHandler)
        : _port(port), _quit(false),
        _listen_sock(new Socket()),
        _epoller(new Epoller()),
        _upperHandler(upperHandler)
    { }

    ~EpollServer()
    { }

    void AddConnection(int fd, uint32_t event, func_t recv_cb, func_t send_cb, func_t except_cb, \
        std::string ip = "0.0.0.0", uint16_t port = 0)
    {
        SetNoWaitOrDie()(fd); //设置fd非阻塞
        std::shared_ptr<Connection> newConnection(new Connection(fd, event, recv_cb, send_cb, except_cb, ip, port));
        _connections.insert(std::make_pair(fd, newConnection));
    }

    void Init()
    {
        // 创建监听套接字
        _listen_sock->Sock();
        _listen_sock->Bind(_port);
        _listen_sock->Listen();

        AddConnection(_listen_sock->Fd(), EPOLLIN | EPOLLET, \
            std::bind(&EpollServer::Accpter, this, std::placeholders::_1), \
            nullptr, nullptr);
        _epoller->EpollerUpdate(EPOLL_CTL_ADD, _listen_sock->Fd(), EPOLLIN | EPOLLET);
    }

    void Accpter(std::shared_ptr<Connection> ListenConnection)
    {
        while(true)
        {
            std::string peerip;
            uint16_t peerport;
            int newfd = _listen_sock->Accept(&peerip, &peerport);
            // int ret = std::accept(ListenConnection->Fd(), )
            // struct epoll_event event;
            // event.events = EPOLLIN;
            // event.data.fd = newfd;
            if(newfd > 0)
            {
                //将newfd添加至epoll模型
                _epoller->EpollerUpdate(EPOLL_CTL_ADD, newfd, EPOLLIN | EPOLLET);
                //添加连接
                AddConnection(newfd, EPOLLIN | EPOLLET, \
                    std::bind(&EpollServer::Recver, this, std::placeholders::_1), \
                    std::bind(&EpollServer::Sender, this, std::placeholders::_1), \
                    std::bind(&EpollServer::Excepter, this, std::placeholders::_1), \
                    peerip, peerport);
                std::cout << "get a new link, ip: " << peerip << ", port: " << peerport
                        << ", fd: " << newfd << std::endl;
            }
            else
            {
                if(errno == EWOULDBLOCK)
                {
                    std::cerr << "accept all done" << std::endl;
                    break;
                }
                else if(errno == EINTR) { continue; }
                else
                {
                    //accept err
                    //accept不设置_except_cb，直接返回
                    return;
                }
            }
        }
    }

    void Recver(std::shared_ptr<Connection> connection)
    {
        int fd = connection->Fd();
        char buff[1024] = { 0 };
        while(true)
        {
            memset(buff, 0, sizeof(buff));
            ssize_t n = read(fd, buff, sizeof(buff) - 1);
            if(n > 0)
            {
                buff[n] = 0;
                connection->InBuff() += buff;
            }
            else if(n == 0)
            {
                //to except
                std::cerr << "peer close, fd: " << fd << std::endl;
                connection->_except_cb(connection);
                return;
            }
            else
            {
                if(errno == EWOULDBLOCK) { break; }
                else if(errno == EINTR) { continue; }
                else {
                    //read err
                    //to except
                    std::cerr << "read err" << std::endl;
                    connection->_except_cb(connection);
                    return;
                }
            }
        }
        //for debug
        // std::cout << "inbuff: " << connection->InBuff() << std::endl;
        _upperHandler(connection); //回调上层数据处理函数
    }

    void Sender(std::shared_ptr<Connection> connection)
    {
        //按需进行写事件等待
        int fd = connection->Fd();
        std::string& outBuff = connection->OutBuff();
        while(true)
        {
            ssize_t n = write(fd, outBuff.c_str(), outBuff.size());
            if(n < 0)
            {
                //to except
                if(errno == EWOULDBLOCK) { break; }
                else if(errno == EINTR) { continue; }
                else {
                    //write err
                    connection->_except_cb(connection);
                    return;
                }
            }
            else if(n > 0)
            {
                outBuff.erase(0, n);
                if(outBuff.empty()) { break; }
            }
            else {
                return; // ?
            }
        }

        if(!outBuff.empty()) {
            EnableEvent(fd, true, true);
        }
        else {
            //取消写事件等待
            EnableEvent(fd, true, false);
        }
    }

    void EnableEvent(int fd, bool readable, bool writeable)
    {
        uint32_t event = 0;
        event |= ((read ? EPOLLIN : 0) | (write ? EPOLLOUT : 0) | EPOLLET);
        _epoller->EpollerUpdate(EPOLL_CTL_MOD, fd, event);
    }

    void Excepter(std::shared_ptr<Connection> connection)
    {
        //统一处理异常情况
        int fd = connection->Fd();
        //在_connections中删除
        _connections.erase(fd);
        std::cout << "move connection, fd: " << fd << std::endl;
        //在epoll模型中删除
        _epoller->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);
        std::cout << "move form epoller, fd: " << fd << std::endl;
        //关闭文件描述符
        close(fd);
        std::cout << "close fd done, fd: " << fd << std::endl;
    }

    bool IsConnectSafe(int fd)
    {
        return _connections.find(fd) == _connections.end() ? \
            false : true;
    }

    void Dispacher(int timeout)
    {
        int cnt = _epoller->EpollerWait(_events, n, timeout);
        for (int i = 0; i < cnt; ++i)
        {
            uint32_t events = _events[i].events;
            int fd = _events[i].data.fd;
            //将异常情况交由读写处理
            if(events & EPOLLERR) {
                events |= (EPOLLIN | EPOLLOUT);
            }
            if(events & EPOLLHUP) {
                events |= (EPOLLIN | EPOLLOUT);
            }
            if((events & EPOLLIN) && IsConnectSafe(fd))
            {
                if(_connections[fd]->_recv_cb != nullptr) {
                    _connections[fd]->_recv_cb(_connections[fd]);
                }
            }
            if((events & EPOLLOUT) && IsConnectSafe(fd))
            {
                if(_connections[fd]->_send_cb != nullptr) {
                    _connections[fd]->_send_cb(_connections[fd]);
                }
            }
        }
    }

    void Loop()
    {
        _quit = false;
        while (true)
        {
            Dispacher(1000); // 事件派发
        }
        _quit = true;
    }

private:
    int _epfd;
    int _port;
    bool _quit;
    std::shared_ptr<Socket> _listen_sock;
    std::shared_ptr<Epoller> _epoller;
    std::unordered_map<int, std::shared_ptr<Connection>> _connections;
    struct epoll_event _events[n];
    func_t _upperHandler;
};